#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "di_test.h"
#include "pcie_mem.h"

enum di_bar0_offset_t {
    DI_GET_ALL_BAR0_OFFSET = 0x30,      /* 读取全部 DI 寄存器偏移 */
    DI_SET_MODE_BAR0_OFFSET = 0x6C,     /* 设置 DI 模式寄存器偏移 */
    DI_INIT_VAL_BAR0_OFFSET = 0x70,     /* 设置 DI 初始值寄存器偏移 */
    DI_SET_ID_BAR0_OFFSET = 0x74,       /* 设置 DI ID 寄存器偏移 */
    DI_GET_FREQ_BAR0_OFFSET = 0x78,     /* 读取 DI 频率寄存器偏移 */
};

enum di_mode_shift_t {
    DI_SET_DELAY_US_SHIFT = 0,      /* DI 延时微秒左移位数 */
    DI_SET_EDGE_SHIFT = 16,         /* DI 边沿左移位数 */
    DI_SET_MODE_SHIFT = 18,         /* DI 模式左移位数 */
};

struct di_info_t {
    uint8_t channel;                    /* 通道号 */
    uint8_t channel_min;                /* 最小通道号 */
    uint8_t channel_max;                /* 最大通道号 */
    uint32_t value;                     /* DI 输入值 */
    uint32_t value_min;                 /* 最小 DI 输入值 */
    uint32_t value_max;                 /* 最大 DI 输入值 */
};

static uint32_t get_all_di(struct pcie_mem_ctx *ctx, uint8_t channel)
{
    volatile uint32_t *addr;
    uint32_t value;

    if (!ctx->map_base) {
        printf("错误: 内存未映射!\n");
        return 0;
    }

    addr = (volatile uint32_t *)((uintptr_t)ctx->map_base);
    *(volatile uint32_t *)((uintptr_t)addr + DI_SET_ID_BAR0_OFFSET) = channel;
    /* 注意：这里需要将字节偏移转换为 uint32_t 偏移 */
    value = *(addr + (DI_GET_ALL_BAR0_OFFSET / sizeof(uint32_t)));

    printf("读取: reg=0x%04X, val=0x%08X\n",
            DI_GET_ALL_BAR0_OFFSET, value);

    return value;
}

uint32_t get_all_di_test(struct pcie_mem_ctx *ctx, struct di_info_t di_info)
{
    uint32_t reg_val = 0;
    uint8_t i = 0;

    if ((di_info.channel >= di_info.channel_min) && (di_info.channel <= di_info.channel_max)) {
        reg_val = get_all_di(ctx, di_info.channel);
        printf("读取全部通道的 DI 输入值为:\n");
        for (i = 0; i <= di_info.channel_max; i++) {
            printf("bit%02d ", i);
        }
        printf("\n");
        for (i = 0; i <= di_info.channel_max; i++) {
            printf("val:%1d ", (reg_val >> i) & 0x1);
        }
        printf("\n");
        reg_val = (reg_val >> di_info.channel) & 0x1;
        printf("读取通道 %d 的 DI 输入值为 %d\n", di_info.channel, reg_val);
    } else {
        reg_val = get_all_di(ctx, di_info.channel);
        printf("读取全部通道的 DI 输入值为:\n");
        for (i = 0; i <= di_info.channel_max; i++) {
            printf("bit%02d ", i);
        }
        printf("\n");
        for (i = 0; i <= di_info.channel_max; i++) {
            printf("val:%1d ", (reg_val >> i) & 0x1);
        }
        printf("\n");
    }
    return reg_val;
}

static int usage(int argc, char *argv[])
{
    if ((argc < 2) || (argc > 3)) {
        printf("用法: %s %s channel\n", argv[0], argv[1]);
        printf("示例: %s %s 0\n", argv[0], argv[1]);
        printf("用法: %s %s n", argv[0], argv[1]);
        printf("示例: %s %s \n", argv[0], argv[1]);
        return EXIT_FAILURE;
    } else {
        return EXIT_SUCCESS;
    }
}

int di_test(int argc, char *argv[])
{
    int ret = 0;
    const char *device_address = PCIE_DI_DEVICE_ADDRESS;
    uint32_t base_addr = PCIE_DI_BASE_ADDR_BAR0;
    size_t map_size = PCIE_DI_BAR0_MAP_SIZE;
    struct pcie_mem_ctx ctx = {
        .fd = -1,
        .map_base = NULL,
        .phys_base = 0,
        .map_size = 0
    };
    struct di_info_t di_info = {
        .channel = 0,
        .channel_min = 0,
        .channel_max = 15,
        .value = 0,
        .value_min = 0,
        .value_max = 0x0000FFFF,
    };

    if (EXIT_SUCCESS != usage(argc, argv)) {
        return EXIT_FAILURE;
    }

    if (argc == 3) {
        di_info.channel = atoi(argv[2]);
    } else if (argc == 2) {
        di_info.channel = di_info.channel_max + 1;
    }

    printf("正在获取通道 %d 的电平...\n",
            di_info.channel);

    if (true == is_pcie_device_enabled(device_address)) {
        printf("设备已启用\n");
    } else {
        printf("设备未启用\n");
        if (enable_pcie_device(device_address) == 0) {
            printf("设备启用成功\n");
        } else {
            printf("设备启用失败!\n");
            return EXIT_FAILURE;
        }
    }

    ret = init_pcie_mem(&ctx, base_addr, map_size);
    if (ret < 0) {
        printf("初始化内存映射失败!\n");
        return EXIT_FAILURE;
    }

    get_all_di_test(&ctx, di_info);

    cleanup_pcie_mem(&ctx);

    printf("通道 %d 的测试已完成\n", di_info.channel);

    return EXIT_SUCCESS;
}
